/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年8月5日
 */
package com.massyframework.beanskin.runtime.framework.installing.fragment;

import java.io.File;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;

import com.massyframework.beanskin.Module;
import com.massyframework.beanskin.ModuleIdentifier;
import com.massyframework.beanskin.ModuleJarsLocator;
import com.massyframework.beanskin.ModuleRepository;
import com.massyframework.beanskin.RequireLibraryDescriptor;
import com.massyframework.beanskin.assembly.Assembly;
import com.massyframework.beanskin.fragment.Fragment;
import com.massyframework.beanskin.fragment.FragmentBuilder;
import com.massyframework.beanskin.framework.Framework;
import com.massyframework.beanskin.runtime.ModuleRegistry;
import com.massyframework.beanskin.runtime.framework.installing.ModuleClassLoaderBuilderFactory;
import com.massyframework.beanskin.runtime.framework.installing.ModuleScanner;
import com.massyframework.beanskin.runtime.framework.installing.SimpleRequireLibraryIntersetor;
import com.massyframework.beanskin.service.ServiceRepositoryFactory;
import com.massyframework.beanskin.service.secure.SecureCapacity;
import com.massyframework.beanskin.util.classloading.LocalResourcesPath;
import com.massyframework.beanskin.util.classloading.ModuleClassLoader;
import com.massyframework.beanskin.util.classloading.ModuleClassLoaderBuilder;
import com.massyframework.beanskin.util.classloading.ResourceLocateFilterRepository;
import com.massyframework.beanskin.util.localstore.LocalStore;

/**
 * 片段安装器
 * @author huangkh
 *
 */
public class FragmentInstaller {
	
	/**
	 * 
	 */
	public FragmentInstaller() {
		
	}

	/**
	 * 加载所有片段构建器
	 * @param kernel {@link Assembly},装配件
	 * @param localStore {@link LocalStore},本地存储
	 * @return {@link Map},片段模块构建器映射
	 */
	public Map<ModuleIdentifier, FragmentBuilder> loadingFragmentBuilders(Assembly assembly, LocalStore localStore) {
		Path[] roots = this.getRoots(localStore);
		return this.loadFragments(assembly, localStore, roots);
	}
	
	/**
	 * 加载并解析指定路经下的片段配置文件
	 * @param assembly {@link Assembly},装配件
	 * @param localStore {@link LocalStore}, 本地存储
	 * @param path {@link Path}, 片段配置文件所在目录
	 * @return {@link FragmentBuilder}, 可能返回null.
	 */
	public FragmentBuilder loadingFragmentBuilder(Assembly assembly, 
			LocalStore localStore,
			ModuleIdentifier moduleIdentifier, 
			Path path) throws Exception {
		Objects.requireNonNull(assembly, "\"assembly\" cannot be null.");
		Objects.requireNonNull(moduleIdentifier, "\"moduleIdentifier\" cannot be null.");
		Objects.requireNonNull(path, "\"path\" cannot be null.");
		
		File dir = path.toFile();
		File xmlFile = new File(dir, "fragment.xml");
		if (xmlFile.exists()) {
			FragmentXmlParser parser = new FragmentXmlParser(assembly, localStore);
			return parser.parse(moduleIdentifier, xmlFile);
		}
		
		return null;
	}
	
	/**
	 * 加载并解析指定路经下的片段配置文件
	 * @param assembly {@link Assembly},装配件
	 * @param localStore {@link LocalStore}, 本地存储
	 * @param path {@link Path}, 片段配置文件所在目录
	 * @return {@link FragmentBuilder}, 可能返回null.
	 */
	public void loadingFragmentXml(Assembly assembly,
			LocalStore localStore,
			ModuleIdentifier moduleIdentifier, 
			Path path,
			FragmentBuilder builder) throws Exception {
		Objects.requireNonNull(assembly, "\"assembly\" cannot be null.");
		Objects.requireNonNull(moduleIdentifier, "\"moduleIdentifier\" cannot be null.");
		Objects.requireNonNull(path, "\"path\" cannot be null.");
		Objects.requireNonNull(builder, "\"builder\" cannot be null.");
		
		File dir = path.toFile();
		File xmlFile = new File(dir, "fragment.xml");
		if (xmlFile.exists()) {
			FragmentXmlParser parser = new FragmentXmlParser(assembly, localStore, ()-> builder);
			parser.parse(moduleIdentifier, xmlFile);
		}
	}
	
	/**
	 * 批量安装片段
	 * @param builders {@link List}, 片段构建器
	 * @param installing {@link boolean},是否伴随安装部署
	 * @param moduleJarsLocator {@link ModuleJarLocator},模块jar定位器
	 * @param moduleRepository {@link ModuleRepository},模块资源库
	 * @param serviceRepositoryFactory, {@link ServiceRepositoryFactory}, 服务资源库工厂
	 * @param framework {@link Framework},运行框架
	 * @param kernel {@link Assembly},内核
	 * @param executor {@link ExecutorService},执行服务
	 */
	public void installFragments(List<FragmentBuilder> builders,
			boolean installing, ModuleJarsLocator moduleJarsLocator, 
			ModuleRepository moduleRepository, ServiceRepositoryFactory serviceRepositoryFactory,  
			Framework framework, Assembly kernel, ExecutorService executor) {
		for (FragmentBuilder builder: builders) {
			executor.execute(
					new Task(builder, installing, moduleJarsLocator, moduleRepository, 
							serviceRepositoryFactory, framework, kernel)
			);
		}
	}
		
	/**
	 * 创建并注册片段
	 * @param builder {@link FragmentBuilder}, 片段构建器
	 * @param installing {@link boolean}, 是否伴随安装部署
	 * @param moduleJarsLocator {@link ModuleJarsLocator},模块jars定位器
	 * @param moduleRepository {@link ModuleRepository},模块资源库
	 * @param serviceRepositoryFactory {@link ServiceRepositoryFactory}
	 * @param framework {@link Framework},运行框架
	 * @param kernel {@link Assembly},内核
	 * @return {@link Fragment}
	 */
	public Fragment createAndRegistFragment(FragmentBuilder builder,
			boolean installing,
			ModuleJarsLocator moduleJarsLocator, 
			ModuleRepository moduleRepository, 
			ServiceRepositoryFactory serviceRepositoryFactory,  
			Framework framework, 
			Assembly kernel) {
		ModuleRegistry moduleRegistry = (ModuleRegistry) moduleRepository;
		
		ModuleClassLoader classLoader =
				createFragmentClassLoader(builder, moduleJarsLocator, moduleRepository, kernel);
		Fragment fragment = builder.moduleRepository(moduleRepository)
				.classLoader(classLoader)
				.serviceRepositoryFactory(serviceRepositoryFactory)
				.build();
		moduleRegistry.register(fragment, installing);		
		return fragment;
	}
	
	/**
	 * 创建片段
	 * @param builder {@link FragmentBuilder}, 片段构建器
	 * @param moduleJarsLocator {@link ModuleJarsLocator},模块jars定位器
	 * @param moduleRepository {@link ModuleRepository},模块资源库
	 * @param serviceRepositoryFactory {@link ServiceRepositoryFactory}
	 * @param framework {@link Framework},运行框架
	 * @param kernel {@link Assembly},内核
	 * @return {@link Fragment}
	 */
	public Fragment createFragment(FragmentBuilder builder,
			ModuleJarsLocator moduleJarsLocator,
			ModuleRepository moduleRepository, 
			ServiceRepositoryFactory serviceRepositoryFactory,  
			Framework framework, 
			Assembly kernel) {		
		ModuleClassLoader classLoader =
				createFragmentClassLoader(builder, moduleJarsLocator, moduleRepository, kernel);
		return  builder.moduleRepository(moduleRepository)
				.classLoader(classLoader)
				.serviceRepositoryFactory(serviceRepositoryFactory)
				.build();
	}
	
	/**
	 * 创建片段类加载器
	 * @param builder {@link FragmentBuilder},构建器
	 * @param moduleJarsLocator {@link ModuleJarsLocator},模块jars定位器
	 * @param moduleRepo {@link ModuleRepository},模块资源库
	 * @return {@link ModuleClassLoader}
	 */
	protected ModuleClassLoader createFragmentClassLoader(FragmentBuilder builder, 
			ModuleJarsLocator moduleJarsLocator, ModuleRepository moduleRepo, Assembly kernel) {
		//jar包
		List<URL> urls = moduleJarsLocator.locate(builder.getModuleIdentifier());
		SimpleRequireLibraryIntersetor intersetor = new SimpleRequireLibraryIntersetor();
		List<RequireLibraryDescriptor> list = intersetor.intersection(
				builder.getModuleIdentifier(), builder.getRequires(), moduleRepo);
		
		List<LocalResourcesPath> paths = new ArrayList<>();
		for (RequireLibraryDescriptor require: list) {
            Module<?> module = moduleRepo.findModuleWithOptimal(require);
            if (module != null) {
                LocalResourcesPath lrp = module.getModuleClassLoader().getLocalResourcePath();
                if (lrp != null) {
                	paths.add(lrp);
                }
            }
        }
		
		ResourceLocateFilterRepository filterRepo =
				kernel.getAssemblyContext().getBean(ResourceLocateFilterRepository.class.getName(), 
						ResourceLocateFilterRepository.class);
		
		ModuleClassLoaderBuilder loaderBuilder =
				ModuleClassLoaderBuilderFactory.createModuleClassLoaderBuilder(kernel);
		SecureCapacity support = (SecureCapacity)builder;
		ModuleClassLoader result =
				loaderBuilder.publicKey(support.getPublicKey())
					.decryptClassNames(support.getDecryptClassNames())
					.decryptScriptFileNames(support.getDescryptScriptFileNames())
					.certificate(support.getX509Name(), support.getX509Encrypted())
					.parentClassLoader(Framework.class.getClassLoader())
					.moduleIdentifier(builder.getModuleIdentifier())
					.addURLs(urls)
					.addLocalResourcesPaths(paths)
					.hotDeployment(true)
					.resourceLocateFilterRepository(filterRepo)
					.build();
				
		return result;
	}
	
	/**
	 * 加载片段库
	 * @param roots {@link Paths}数组
	 * @param localStore {@link LocalStore}, 本地存储
	 * @param roots {@link Path[]},根路经
	 * @return {@link Map}
	 */
	protected Map<ModuleIdentifier, FragmentBuilder> loadFragments(
			Assembly kernel, LocalStore localStore, Path[] roots){
		ModuleScanner scanner = new ModuleScanner(roots);
		return scanner.scanning("fragment.xml", new FragmentXmlParser(kernel, localStore));
	}
	
	
	/**
	 * 扫描的根目录
	 * @param localStore {@link LocalStore}
	 * @return {@link Path}数组.
	 */
	protected Path[] getRoots(LocalStore localStore) {
		return new Path[] {localStore.getDeploymentDirectory(null)};
	}
	
	private class Task implements Runnable {
		private FragmentBuilder builder;
		private boolean installing;
		private ModuleJarsLocator moduleJarsLocator;
		private ModuleRepository moduleRepository;
		private ServiceRepositoryFactory serviceRepositoryFactory;
		private Framework framework;
		private Assembly kernel;
		
		/**
		 * 构造方法
		 * @param moduleJarsLocator
		 * @param moduleRepository
		 * @param serviceRepositoryFactory
		 * @param framework
		 * @param kernel
		 */
		public Task (
				FragmentBuilder builder,
				boolean installing,
				ModuleJarsLocator moduleJarsLocator, 
				ModuleRepository moduleRepository, 
				ServiceRepositoryFactory serviceRepositoryFactory,  
				Framework framework, 
				Assembly kernel) {
			this.builder = builder;
			this.installing = installing;
			this.moduleJarsLocator = moduleJarsLocator;
			this.moduleRepository = moduleRepository;
			this.serviceRepositoryFactory = serviceRepositoryFactory;
			this.framework = framework;
			this.kernel = kernel;
		}

		@Override
		public void run() {
			createAndRegistFragment(this.builder, 
					this.installing,
					this.moduleJarsLocator, 
					this.moduleRepository, 
					this.serviceRepositoryFactory, 
					this.framework, 
					this.kernel);
		}
		
		
	}
}
